Python

De computertaal Python werd ooorspronkelijk ontworpen als een open source computertaal die voor iedereen makkelijk te leren en te programmeren zou zijn. Dat verklaart de enorme populariteit ervan. Maar door die enorme populariteit is het in de loop der tijd toch ook een heel complexe taal geworden. Er zijn eindeloos veel boeken verschenen over Python. De inhoud van deze boeken wil ik niet herhalen. Deze pagina gaat ervanuit dat je met de eerste beginselen van de taal al kennisgemaakt hebt. Op deze webpagina verzamel ik allerlei weetjes die ik makkelijk wil kunnen terugvinden.

User input

Met de functie input() kun je iemand iets laten intypen. Wat de gebruiker intikt wordt altijd als string geïnterpreteerd. Mocht je willen dat wat ingetikt wordt anders wordt opgevat, moet je het ingetikte converteren.

def main():
    x = input('x = ')
    print(type(x))
    if x.isnumeric():
        x = int(x)
        print(type(x))

if __name__ == '__main__':
    main()

Gegevenstypen

Python kent verschillende soorten gegevens. De meest bekende zijn:

letterscharactersstr()
gehele getallenintegersint()
drijvende-komma-getallenfloating point numbersfloat()
complexe getallencomplex numbers
waar of niet waarBooleans

In Python wordt geen decimale komma gebruikt, maar een decimale punt.

Hexadecimale en decimale representatie

def main():
    x = 'a'

    print( ord(x) )                     # 97
    print( x.encode('utf-8') )          # b'a'
    print( bytes(x, 'utf-8') )          # b'a'
    
    print( hex(ord(x)) )                # 0x61
    print( x.encode('utf-8').hex() )    # 61
    print( bytes(x, 'utf-8').hex() )    # 61

    n = 97

    print( chr(n) )                     # a

    b = b'\x61'

    print( str(b, 'utf-8') )            # a
    print( b.decode('utf-8') )          # a

if __name__ == '__main__':
    main()

Een bestand aanmaken

Een bestand kun je met de volgende code aanmaken:

def main():
    file_object = open('outfile.csv', 'w')
    file_object.write("Hello\n")
    file_object.close()

if __name__ == '__main__':
    main()        

Een bestand inlezen

Voor mijn privé-programma's, vind ik csv-bestanden inlezen de gemakkelijkste vorm van invoer. Csv-bestanden kun je makkelijk aanmaken met Kladblok of Excel. Mijn voorkeur gaat uit naar de puntkomma als scheidingsteken. Het inlezen van een bestand kan in Python op verschillende manieren. De eerste manier is door een bestand te openen voor lezen, vervolgens het bestand te doorlopen met een for-loop. Bij een csv-bestand kun je in die for-loop elk record splitsen in verschillende velden. Na verwerking moet je het bestand weer te sluiten.

def main():
    file_object = open('myfile.csv', 'r')
    for line in file_object:
        print(line.strip('\n'))
        velden = line.split(';')
        for v in velden:
            print(v)
    file_object.close()
    
if __name__ == '__main__':
    main() 

Een andere manier om een bestand in te lezen gaat met behulp van een with-context. Daarbij hoef je de file niet te sluiten, want de context van het with-statement zorgt ervoor dat dat gebeurt :

import sys

def main():
    filename = 'MyFile.txt'
    try:
        with open(filename) as f_input:
            for line in f_input:
                line = line.strip('\n')
                print(line)
    except Exception as err:
        print('(1) err')
        print( err )
        print('(2) sys.exc_info()[0]')
        print( sys.exc_info()[0] )          # exception class
        print('(3) sys.exc_info()[1]')
        print( sys.exc_info()[1] )          # value
        print('(4) sys.exc_info()[2]')
        print( sys.exc_info()[2] )          # traceback object
        print('=====')         
if __name__ == '__main__':
    main()  

Als het bestand MyFile.txt niet bestaat, geeft het programma de volgende output:

(1) err
[Errno 2] No such file or directory: 'MyFile.txt'
(2) sys.exc_info()[0]
<class 'FileNotFoundError'>
(3) sys.exc_info()[1]
[Errno 2] No such file or directory: 'MyFile.txt'
(4) sys.exc_info()[2]
<traceback object at 0x000002C6B50EB200>
=====

Exceptions

Je kunt het afhandelen vvan de fout in bovenstaand programma wat eleganter laten verlopen door de de class die in bovenstaande foutmelding werd genoemd, afzonderlijk af te handelen:

import sys

def main():
    filename = 'MyFile.txt'
    try:
        with open(filename) as f_input:
            for line in f_input:
                line = line.strip('\n')
                print(line)
    except FileNotFoundError:
        print('Bestand ' + filename + ' is niet aanwezig')
    except Exception as err:
        print('(1) err')
        print( err )
        print('(2) sys.exc_info()[0]')
        print( sys.exc_info()[0] )          # exception class
        print('(3) sys.exc_info()[1]')
        print( sys.exc_info()[1] )          # value
        print('(4) sys.exc_info()[2]')
        print( sys.exc_info()[2] )          # traceback object
        print('=====')         
if __name__ == '__main__':
    main()  

Als je de html-file die gaat over CSS, als invoerbestand neemt, wordt het programma maar deels uitgevoerd. Het eindigt met de volgende regels:

            Als de viewport breed genoeg is, staan de verschillende blokken naast elkaar.
(1) err
'charmap' codec can't decode byte 0x9d in position 3699: character maps to 
(2) sys.exc_info()[0]

(3) sys.exc_info()[1]
'charmap' codec can't decode byte 0x9d in position 3699: character maps to 
(4) sys.exc_info()[2]
<traceback object at 0x000001FA93C32E80>
=====

Deze informatie is niet voldoende om te weten wat er aan de hand is. We breiden de code uit:

import sys, traceback

def main():
    filename = 'MyFile.txt'
    try:
        with open(filename) as f_input:
            for line in f_input:
                line = line.strip('\n')
                print(line)
    except FileNotFoundError:
        print('Bestand ' + filename + ' is niet aanwezig')
    except Exception as err:
        print('(1) err')
        print( err )
        print('(2) sys.exc_info()[0]')
        print( sys.exc_info()[0] )          # exception class
        print('(3) sys.exc_info()[1]')
        print( sys.exc_info()[1] )          # value
        print('(4) sys.exc_info()[2]')
        print( sys.exc_info()[2] )          # traceback object
        print('(5) err.object')
        print( err.object )
        print('(6) traceback.print_exception(err)')
        traceback.print_exception(err)
        print('(7) traceback.print_tb(err)')
        traceback.print_tb(err)             # traceback
        print('=====')         
if __name__ == '__main__':
    main()  

De output wordt:

            Als de viewport breed genoeg is, staan de verschillende blokken naast elkaar.
(1) err
'charmap' codec can't decode byte 0x9d in position 3699: character maps to <undefined>
(2) sys.exc_info()[0]
<class 'UnicodeDecodeError'>
(3) sys.exc_info()[1]
'charmap' codec can't decode byte 0x9d in position 3699: character maps to <undefined>
(4) sys.exc_info()[2]
<traceback object at 0x0000022A10433940>
(5) err.object
Squeezed text (64 lines).
(6) traceback.print_exception(err)
Traceback (most recent call last):
  File "E:\Site\Site20240516\prive\ict\extra\test0001.py", line 7, in main
    for line in f_input:
  File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\encodings\cp1252.py", line 23, in decode
    return codecs.charmap_decode(input,self.errors,decoding_table)[0]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3699: character maps to <undefined>
(7) traceback.print_tb(err)
Traceback (most recent call last):
  File "E:\Site\Site20240516\prive\ict\extra\test0001.py", line 7, in main
    for line in f_input:
  File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\encodings\cp1252.py", line 23, in decode
    return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3699: character maps to <undefined>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "E:\Site\Site20240516\prive\ict\extra\test0001.py", line 29, in <module>
    main()
  File "E:\Site\Site20240516\prive\ict\extra\test0001.py", line 26, in main
    traceback.print_tb(err)             # traceback
  File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\traceback.py", line 55, in print_tb
    print_list(extract_tb(tb, limit=limit), file=file)
  File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\traceback.py", line 74, in extract_tb
    return StackSummary._extract_from_extended_frame_gen(
  File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\traceback.py", line 418, in _extract_from_extended_frame_gen
    for f, (lineno, end_lineno, colno, end_colno) in frame_gen:
  File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\traceback.py", line 355, in _walk_tb_with_full_positions
    positions = _get_code_position(tb.tb_frame.f_code, tb.tb_lasti)
AttributeError: 'UnicodeDecodeError' object has no attribute 'tb_frame'

Merk op dat het statement "print('=====')" niet wordt uitgevoerd. Het programma is na het afdrukken van de foutmeldingen afgebroken.

Deze foutmelding is te complex voor mij. Ik ga op zoek op Internet, en ontdek dat anderen al meer dan 6 jaar eerder met deze foutmelding worstelden. Als mogelijke oorzaak wordt geopperd dat het bestand niet met utf-8 is aangemaakt. Ik heb de input-file aangemaakt met behulp van het Kladblok-programma in Windows. Windows schijnt niet met utf-8 te werken. De suggesties die op stackoverklow.com worden gegeven, zijn:

De eerste suggestie leidt tot:

import sys, traceback

def main():
    filename = 'MyFile.txt'
    tel = 0
    try:
        with open(filename, 'rb') as f_input:
            for b_line in f_input:
                line = b_line.decode('utf-8')
                line = line.strip('\n')
                print(line)
    except FileNotFoundError:
        print('Bestand ' + filename + ' is niet aanwezig')
    except Exception as err:
        print('(1) err')
        print( err )
        print('(2) sys.exc_info()[0]')
        print( sys.exc_info()[0] )          # exception class
        print('(3) sys.exc_info()[1]')
        print( sys.exc_info()[1] )          # value
        print('(4) sys.exc_info()[2]')
        print( sys.exc_info()[2] )          # traceback object
        print('(5) err.object')
        print( err.object )
        print('(6) traceback.print_exception(err)')
        traceback.print_exception(err)
        print('(7) traceback.print_tb(err)')
        traceback.print_tb(err)             # traceback
        print('=====')         
if __name__ == '__main__':
    main()  

While-statement

Het volgende programma telt een aantal getallen bij elkaar op.

def main():
    numbers = [1, 2, 3, 5, 7, 11, 
               13, 17, 19, 23, 29]
    quantity = len(numbers)
    total = 0
    i = 0
    while i < quantity:
        total = total + numbers[i]
        i = i + 1
    print(quantity)
    print(total)

if __name__ == '__main__':
    main()








In het while-statement wordt de voorwaarde 'i < quantity' getest. Er zijn twee plaatsen die voorafgaand aan deze test kunnen worden uitgevoerd. Beide zorgen ervoor dat de voorwaarde het juiste resultaat oplevert. Dat zijn het statement 'i = 0' voorafgaand aan het while-statement en het statement 'i = i + 1' aan het einde van block dat wordt uitgevoerd als de voorwaarde True oplevert.

Een HTML-bestand aanmaken

Aanleiding

Ik wil bepaalde plaatjes maken met SVG. Ik zie in mijn fanatsieën allerlei plaatjes met rechthoeken die verbonden worden door pijlen. Als ik veel van dergelijke plaatjes aan mijn website wil toevoegen, wil ik een programma dat mij daarbij helpt. Ik ga kijken of het mij lukt zo'n programma te maken.

Versie 1

Een html-file met een svg-plaatje met een rechthoek kun je als volgt maken:

def main():
    svg_tekst="the quick brown fox jumps over the lazy dog"
    html_title = "SVG rechthoek met tekst"
    file_object = open("outfile.html",  "w")
    file_object.write("<!DOCTYPE html>\n")
    file_object.write("<html>\n")
    file_object.write(4*" " + "<head>\n")
    file_object.write(8*" " + "<title>")
    file_object.write(html_title)
    file_object.write("</title>\n")
    file_object.write(4*" " + "</head>\n")
    file_object.write(4*" " + "<body>\n")
    file_object.write(8*' ' + '<svg width="350" height="50">\n')
    line = 12*' ' + '<rect x="10" y="5" '
    line += 'width="300" height="30" fill="lightGreen" />\n'
    file_object.write(line)
    line = 12*' ' + '<text x="15" y="24">' 
    line += svg_tekst + '</text>\n'
    file_object.write(line)
    file_object.write(8*' ' + '</svg>\n')
    file_object.write("    </body>\n")
    file_object.write("</html>\n")
    file_object.close
main()

Een dergelijke opzet van het programma werkt wel, maar het is behoorlijk onleesbaar. Bij elke regel moet je bedenken, wat voor effect die regel heeft als het programma wordt gedraaid. Ook later is het ontzettend lastig om je te herinneren wat dit programma ook al weer deed.

Versie 2

Beter is de programma-regels die op een bepaalde manier bij elkaar horen, op te nemen in één functie. We nemen de regels die horen bij het maken van een html-bestand op in de functie html_start, we nemen de regels die horen bij het afsluiten van het html-bestand op in de functie html_end en we nemen de regels die horen bij het tekenen van de rechthoek en het plaatsen een tekst in de rechthoek op in de functie svg. Als we dat doen, ziet het programma er als volgt uit:

def html_start(html_title, file_object):             
    file_object.write("<!DOCTYPE html>\n")
    file_object.write("<html>\n")
    file_object.write(4*" " + "<head>\n")
    file_object.write(8*" " + "<title>")
    file_object.write(html_title)
    file_object.write("</title>\n")
    file_object.write(4*" " + "</head>\n")
    file_object.write(4*" " + "<body>\n")

def html_end(file_object):
    file_object.write("    </body>\n")
    file_object.write("</html>\n")

def svg(svg_tekst, file_object):
    file_object.write(8*' ' + '<svg width="350" height="50">\n')
    line = 12*' ' + '<rect x="10" y="5" '
    line += 'width="300" height="30" fill="lightGreen" />\n'
    file_object.write(line)
    line = 12*' ' + '<text x="15" y="24">' 
    line += svg_tekst + '</text>\n'
    file_object.write(line)
    file_object.write(8*' ' + '</svg>\n')
    
def main():
    svg_tekst="the quick brown fox jumps over the lazy dog"
    html_title = "SVG rechthoek met tekst"
    file_object = open("outfile.html",  "w")
    html_start(html_title, file_object)
    svg(svg_tekst, file_object)
    html_end(file_object)
    file_object.close

if __name__ == '__main__':
    main()
































Met def-commando's worden eerst de functies html_start, html_end,svg en main aangemaakt. In de laatste regel van het programma wordt de functie main gestart. In de functie main worden eerst twee variabelen svg_tekst en html_title gevuld. Dan wordt er een file_object voor het bestand outfile.html gecreëerd. Op de volgende regels worden de functies html_start, svg en html_end uitgevoerd. Deze functies voegen de gewenste html- en svg-code toe aan het bestand outfile.html. Tenslotte wordt het file_object weer gesloten.

Dit programma genereert een html-file met de volgende inhoud:

<!DOCTYPE html>
<html>
    <head>
        <title>SVG rechthoek met tekst</title>
    </head>
    <body>
        <svg width="350" height="50">
            <rect x="10" y="5"  width="300" height="30" fill="lightGreen" />
            <text x="15" y="24">the quick brown fox jumps over the lazy dog</text>
        </svg>
    </body>
</html>

Door een browser wordt dit svg-plaatje weergegeven als:

Versie 3

Ik wil een beter onderscheid tussen de regels die te maken hebben met het tekenen van een svg-plaatje in zijn geheel en het tekenen van een onderdeel van het plaatje namelijk de rechthoek. Ik splits daartoe de functie svg in drie delen: svg_start, rectangle en svg_end.

def html_start(html_title, file_object):             
    file_object.write("<!DOCTYPE html>\n")
    file_object.write("<html>\n")
    file_object.write(4*" " + "<head>\n")
    file_object.write(8*" " + "<title>")
    file_object.write(html_title)
    file_object.write("</title>\n")
    file_object.write(4*" " + "</head>\n")
    file_object.write(4*" " + "<body>\n")

def html_end(file_object):
    file_object.write("    </body>\n")
    file_object.write("</html>\n")

def svg_start(file_object): 
    line = 8*' ' + '<svg width="350" height="50">\n'
    file_object.write(line)

def svg_end(file_object): 
    file_object.write(8*' ' + '</svg>\n')
    
def rectangle(svg_tekst, file_object): 
    line = 12*' ' + '<rect x="10" y="5" '
    line += 'width="300" height="30" '
    line += 'fill="lightGreen" />\n'
    file_object.write(line)
    line = 12*' ' + '<text x="15" y="24">' 
    line += svg_tekst + '</text>\n'
    file_object.write(line)
    
def main():
    svg_tekst="the quick brown fox jumps "
    svg_tekst += "over the lazy dog"
    html_title = "SVG rechthoek met tekst"
    file_object = open("outfile.html",  "w")
    html_start(html_title, file_object)  
    svg_start(file_object)  
    rectangle(svg_tekst, file_object) 
    svg_end(file_object) 
    html_end(file_object)
    file_object.close

if __name__ == '__main__':
    main()





















De functies svg_start en svg_end hebben enkel te maken met een svg-plaatje, ongeacht wat daarin wordt getekend. De functie heeft enkel te maken met het tekenen van een rechthoek.

Parametrizeren

In de bovenstaande code gaan we uitzoeken in welke parameters vaste waarden zijn ingevuld. In het volgende zijn deze vaste waarden met rode letters aangegeven:

def html_start(html_title, file_object):             
    file_object.write("<!DOCTYPE html>\n")
    file_object.write("<html>\n")
    file_object.write(4*" " + "<head>\n")
    file_object.write(8*" " + "<title>")
    file_object.write(html_title)
    file_object.write("</title>\n")
    file_object.write(4*" " + "</head>\n")
    file_object.write(4*" " + "<body>\n")

def html_end(file_object):
    file_object.write("    </body>\n")
    file_object.write("</html>\n")

def svg_start(file_object): 
    line = 8*' ' + '<svg width="350" height="50">\n'
    file_object.write(line)

def svg_end(file_object): 
    file_object.write(8*' ' + '</svg>\n')
    
def rectangle(svg_tekst, file_object): 
    line = 12*' ' + '<rect x="10" y="5" '
    line += 'width="300" height="30" '
    line += 'fill="lightGreen" />\n'
    file_object.write(line)
    line = 12*' ' + '<text x="15" y="24">' 
    line += svg_tekst + '</text>\n'
    file_object.write(line)
    
def main():
    svg_tekst="the quick brown fox jumps "
    svg_tekst += "over the lazy dog"
    html_title = "SVG rechthoek met tekst"
    file_object = open("outfile.html",  "w")
    html_start(html_title, file_object)  
    svg_start(file_object)  
    rectangle(svg_tekst, file_object) 
    svg_end(file_object) 
    html_end(file_object)
    file_object.close

if __name__ == '__main__':
    main()




































De output-file die dit programma genereert is de html-file met de naam outfile.html. Als je deze file met de browser laat zien, krijg je:

Ik geef in dit plaatje de waarden van een aantal van de parameters aan:

Versie 4

Ik wil deze rechthoek een andere plaats in het plaatje kunnen geven, en ik wil meer dan één rechthoek in een plaatje kunnen opnemen. Daarom verander ik de gevonden vast ingevulde waarden in parameters.

def html_start(html_title, file_object):             
    file_object.write("<!DOCTYPE html>\n")
    file_object.write("<html>\n")
    file_object.write(4*" " + "<head>\n")
    file_object.write(8*" " + "<title>")
    file_object.write(html_title)
    file_object.write("</title>\n")
    file_object.write(4*" " + "</head>\n")
    file_object.write(4*" " + "<body>\n")

def html_end(file_object):
    file_object.write("    </body>\n")
    file_object.write("</html>\n")

def svg_start(width, height, file_object): 
    line = 8*' ' + '<svg width="' + str(width) + '" '
    line += 'height="' + str(height) + '">\n'
    file_object.write(line)

def svg_end(file_object): 
    file_object.write(8*' ' + '</svg>\n')
    
def rectangle(rect_x, rect_y, width, height, rect_color, 
               text_x, text_y, svg_tekst, file_object): 
    line = 12*' ' + '<rect x="' + str(rect_x) + '" '
    line += ' y="' + str(rect_y) + '" '
    line += 'width="' + str(width) + '" '
    line += 'height="' + str(height) + '" '
    line += 'fill="' + rect_color + '" />\n'
    file_object.write(line)
    line = 12*' ' + '<text x="' + str(text_x) + '" '
    line += 'y="' + str(text_y) + '">' 
    line += svg_tekst + '</text>\n'
    file_object.write(line)
    
def main():
    svg_tekst = "the quick brown fox jumps "
    svg_tekst += "over the lazy dog"
    html_title = "SVG rechthoek met tekst"
    file_object = open("outfile.html",  "w")
    html_start(html_title, file_object)  
    svg_start(350, 50, file_object)  
    rectangle(10, 5, 300, 30, "lightGreen", 
              15, 24, svg_tekst, file_object) 
    svg_end(file_object) 
    html_end(file_object)
    file_object.close

if __name__ == '__main__':
    main()






































De waarden in de functies svg_start en rectangle werden toegekend, worden nu als parameter aan de functies doorgegeven vanuit de functie main.

versie 5

In de functie main is het nu niet in één oogopslag duidelijk, wat de parameters in de aanroep van de functies svg_start en rectangle voorstellen. Ik ga daarom die vaste waarden opnemen in variabelen.

def html_start(html_title, file_object):             
    file_object.write("<!DOCTYPE html>\n")
    file_object.write("<html>\n")
    file_object.write(4*" " + "<head>\n")
    file_object.write(8*" " + "<title>")
    file_object.write(html_title)
    file_object.write("</title>\n")
    file_object.write(4*" " + "</head>\n")
    file_object.write(4*" " + "<body>\n")

def html_end(file_object):
    file_object.write("    </body>\n")
    file_object.write("</html>\n")
def svg_start(width, height, file_object): 
    line = 8*' ' + '<svg width="' + str(width) + '" '
    line += 'height="' + str(height) + '">\n'
    file_object.write(line)

def svg_end(file_object): 
    file_object.write(8*' ' + '</svg>\n')
    
def rectangle(rect_x, rect_y, 
              width, height, rect_color, 
              text_x, text_y, svg_tekst, 
              file_object): 
    line = 12*' ' + '<rect x="' + str(rect_x) + '" '
    line += ' y="' + str(rect_y) + '" '
    line += 'width="' + str(width) + '" '
    line += 'height="' + str(height) + '" '
    line += 'fill="' + rect_color + '" />\n'
    file_object.write(line)
    line = 12*' ' + '<text x="' + str(text_x) + '" '
    line += 'y="' + str(text_y) + '">' 
    line += svg_tekst + '</text>\n'
    file_object.write(line)
    
def main():
    html_title = "SVG rechthoek met tekst"
    svg_width = 350
    svg_height = 50
    rect_x = 10
    rect_y = 5
    rect_width = 300
    rect_height = 30
    rect_color = "lightGreen"
    rect_text_x = 15
    rect_text_y = 24
    rect_text="the quick brown fox jumps "
    rect_text += "over the lazy dog"
    file_object = open("outfile.html",  "w")
    html_start(html_title, file_object)  
    svg_start(svg_width, svg_height, file_object)  
    rectangle(rect_x, rect_y, 
              rect_width, rect_height,
              rect_color, rect_text_x, 
              rect_text_y, rect_text,
              file_object) 
    svg_end(file_object) 
    html_end(file_object)
    file_object.close

if __name__ == '__main__':
    main()

Het doel van het parametrizeren is, dat je het programma kunt gaan uitbreiden zodat je meer rechthoeken kunt gaan tekenen in één plaatje. Wat je daarmee introduceert is, dat je erop moet letten dat de parameters niet alle waarden kunnen aannemen. Zo kunnen de lengte en de breedte van een rechthoek nooit kleiner dan of gelijk aan nul zijn. In SVG geldt dat niet voor de plaats waar een figuur wordt getekend. De x- en y- waarden mogen wel kleiner of gelijk aan nul zijn.

Versie 6 - het HTML-bestand uitbreiden

We voegen een tweede svg-plaatje toe aan het html-bestand. Het tweede plaatje tekenen we 45 pixels onder het bestaande plaatje. Het svg-plaatje in zijn geheel moet dan iets grotere hoogte krijgen. De code wordt:

def html_start(html_title, file_object):             
    file_object.write("<!DOCTYPE html>\n")
    file_object.write("<html>\n")
    file_object.write(4*" " + "<head>\n")
    file_object.write(8*" " + "<title>")
    file_object.write(html_title)
    file_object.write("</title>\n")
    file_object.write(4*" " + "</head>\n")
    file_object.write(4*" " + "<body>\n")

def html_end(file_object):
    file_object.write("    </body>\n")
    file_object.write("</html>\n")

def svg_start(width, height, file_object): 
    line = 8*' ' + '<svg width="' + str(width) + '" '
    line += 'height="' + str(height) + '">\n'
    file_object.write(line)

def svg_end(file_object): 
    file_object.write(8*' ' + '</svg>\n')
    
def rectangle(rect_x, rect_y, 
              width, height, rect_color, 
              text_x, text_y, svg_tekst, 
              file_object): 
    line = 12*' ' + '<rect x="' + str(rect_x) + '" '
    line += ' y="' + str(rect_y) + '" '
    line += 'width="' + str(width) + '" '
    line += 'height="' + str(height) + '" '
    line += 'fill="' + rect_color + '" />\n'
    file_object.write(line)
    line = 12*' ' + '<text x="' + str(text_x) + '" '
    line += 'y="' + str(text_y) + '">' 
    line += svg_tekst + '</text>\n'
    file_object.write(line)
    
def main():
    html_title = "SVG rechthoeken met tekst"
    svg_width = 350
    svg_height = 100
    rect_x = 10
    rect_y = 5
    rect_width = 300
    rect_height = 30
    rect_color = "lightGreen"
    rect_text_x = 15
    rect_text_y = 24
    rect_text="the quick brown fox jumps "
    rect_text += "over the lazy dog"
    file_object = open("outfile.html",  "w")
    html_start(html_title, file_object)  
    svg_start(svg_width, svg_height, file_object)  
    rectangle(rect_x, rect_y, 
              rect_width, rect_height,
              rect_color, rect_text_x, 
              rect_text_y, rect_text,
              file_object) 
    rect_x = 10
    rect_y = 50
    rect_width = 300
    rect_height = 30
    rect_color = "lightGreen"
    rect_text_x = 15
    rect_text_y = 69
    rect_text="filmquiz bracht knappe ex-yogi"
    rect_text += " van de wijs"
    rectangle(rect_x, rect_y, 
              rect_width, rect_height,
              rect_color, rect_text_x, 
              rect_text_y, rect_text,
              file_object) 
    svg_end(file_object) 
    html_end(file_object)
    file_object.close

if __name__ == '__main__':
    main()

Als je deze code uitvoert, wordt een bestand outfile.html gemaakt met de volgende inhoud:

<!DOCTYPE html>
<html>
    <head>
        <title>SVG rechthoeken met tekst</title>
    </head>
    <body>
        <svg width="350" height="100">
            <rect x="10"  y="5" width="300" height="30" fill="lightGreen" />
            <text x="15" y="24">the quick brown fox jumps over the lazy dog</text>
            <rect x="10"  y="50" width="300" height="30" fill="lightGreen" />
            <text x="15" y="69">filmquiz bracht knappe ex-yogi van de wijs</text>
        </svg>
    </body>
</html>

In een browser wordt dit bestand als volgt weergegeven:

We zien in de main()-functie in bovenstaand programma min of meer zich herhalende code-blokken ontstaan. Het eerste code-blok bevat de statements om de eerste rechthoek weer te geven, het volgende code-blok bevat de statements die de tweede rechthoek weergeven. Dit soort zich herhalende code maakt het bewerkelijk om het programma in de toekomst aan te passen. De herhalende code moet je vermijden. Dit is het DRY-principe, dat staat voor Don't Repeat Yourself.

Versie 7 - zich herhalende code opnemen in een loop

Ik wil de parameters die nodig zijn om de rechthoeken te definiëren kunnen inlezen vanuit een bestand. Het makkelijkste bestandstype dat ik ken om gegevens in op te slaan, is het csv-bestandstype. Ik maak het ASCII-bestand rechthoeken.csv aan met de volgende inhoud:

x;y;width;height;color;text_x;text_y;text;
10;5;300;30;lightGreen;15;24;the quick brown fox jumps over the lazy dog;
10;50;300;30;lightGreen;15;69;filmquiz bracht knappe ex-yogi van de wijs;

Ik wijzig vervolgens het programma. Ik lees de records uit het bestand rechthoeken.csv in, en neem deze records op in een list. Ik gebruik die list om de grootte van het plaatje te berekenen, en vervolgens het svg-bestand met het plaatje weg te schrijven.

def html_start(html_title, file_object):             
    file_object.write("<!DOCTYPE html>\n")
    file_object.write("<html>\n")
    file_object.write(4*" " + "<head>\n")
    file_object.write(8*" " + "<title>")
    file_object.write(html_title)
    file_object.write("</title>\n")
    file_object.write(4*" " + "</head>\n")
    file_object.write(4*" " + "<body>\n")

def html_end(file_object):
    file_object.write("    </body>\n")
    file_object.write("</html>\n")
def svg_start(width, height, file_object): 
    line = 8*' ' + '<svg width="' + str(width) + '" '
    line += 'height="' + str(height) + '">\n'
    file_object.write(line)

def svg_end(file_object): 
    file_object.write(8*' ' + '</svg>\n')
    
def rectangle(rect_x, rect_y, 
              width, height, rect_color, 
              text_x, text_y, svg_tekst, 
              file_object): 
    line = 12*' ' + '<rect x="' + str(rect_x) + '" '
    line += ' y="' + str(rect_y) + '" '
    line += 'width="' + str(width) + '" '
    line += 'height="' + str(height) + '" '
    line += 'fill="' + rect_color + '" />\n'
    file_object.write(line)
    line = 12*' ' + '<text x="' + str(text_x) + '" '
    line += 'y="' + str(text_y) + '">' 
    line += svg_tekst + '</text>\n'
    file_object.write(line)
    
def main():
    filename = 'rechthoeken.csv'
    rectangle_list = []
    width_list = []
    height_list = []
    try:
        with open(filename) as f_input:
            for line in f_input:
                line = line.strip('\n')
                v = line.split(';')
                if v[0] != 'x':
                    parameter_lijst = [ v[0], v[1], v[2], v[3], 
                                        v[4], v[5], v[6], v[7] ]
                    rectangle_list.append(parameter_lijst)
                    width_list.append( int(v[0]) + int(v[2]) )
                    height_list.append( int(v[1]) + int(v[3]) )
    except Exception as err:
        print( err )

    html_title = "SVG rechthoeken met tekst"
    svg_width = max(width_list)
    svg_height = max(height_list)
    file_object = open("outfile.html",  "w")
    html_start(html_title, file_object)  
    svg_start(svg_width, svg_height, file_object)  
    for r in rectangle_list:
         rectangle(r[0], r[1], r[2], r[3],
                  r[4], r[5], r[6], r[7],
                  file_object) 
    svg_end(file_object) 
    html_end(file_object)
    file_object.close

if __name__ == '__main__':
    main()

Nieuwe functionaliteit verkennen

We willen nu een tekst in een rechthoek opnemen, die meerdere regels beslaat. We moeten er dan eerst achterkomen hoe breed en hoe hoog de rechthoek moet zijn, om ervoor te zorgen dat de tekst in de rechthoek past. We maken daartoe een kleine html-file, waarin die tekst voorkomt. Bijvoorbeeld:

<!DOCTYPE html>
<html>
    <head>
        <title>Tekst</title>
        <style>
        .tekst { 
            background-color: lightGreen;
            width: 20em;
        }
        </style>
    </head>
    <body>
        <div class="tekst">
        Als beginnend concertist debuteerde een fijngevoelige gitarist, 
        hierna improviseerden jeugdige klankkunstenaars levendig 
        maar notenblind op Peruviaanse quena’s, robuuste slagwerkers 
        trommelden uitzinnige volksmuziek, waarna xylofonisten "e;Yesterday"e; zongen.
        </div>
    </body>
</html>

Als je deze html-file met een browser weergeeft, zie je:

Je kunt het groene tekstblok wat smaller of breder maken door de waarde van width binnen de style-tag te variëren. Als de grootte en de vorm van de rechthoek je bevalt, kun je kijken hoeveel pixels groot de groene rechthoek is. Je rechtsklikt in het browser-scherm, en kiest vervolgens de optie "Inspecteren". Bijvoorbeeld:

Beweeg dan zonder te klikken de muis over het groene gebied. (Dit heet op zijn engels: hoveren.) Dan verschijnt boven in beeld de grootte van de division (<div>) waarin de tekst zich bevindt.

In dit voorbeeld is dat 315,55 pixels breed en 107 pixels hoog. De breedte van 315,55 ronden we af naar 316.

De tekst wordt over 6 regels verdeeld. Een enkele regel heeft dan een hoogte van 107÷6=17,83333. We ronden dit af naar 18. Door die afronding wordt de totale hoogte van de rechthoek 6×18=108.

We moeten ons realiseren dat we zitten te kijken naar de kleinst mogelijke rechthoek waar de tekst in past. Die kleinste rechthoek noem ik een minimale rechthoek. Misschien willen we de rechthoek wat groter hebben in het plaatje dat we willen gaan maken. We willen marges om de tektst heen toevoegen aan de rechthoek. Deze marges zouden we kunnen aangeven met de woorden top, bottom, left en right.

We moeten ook aangeven waar de tekst wordt vervolgd op een nieuwe regel, want svg kent geen automatisch uitlijnen voor teksten.

Volgorde van berekeningen napluizen

Ik zie twee mogelijke manieren om de plaats van een rechthoek in een plaatje aan te geven. De ene manier is door de plaats van de linker bovenhoek te specificeren, de ander is het midden van de rechthoek te specificeren. Het midden is het snijpunt van de diagonalen. Als je het midden van de rechthoek specificeert, dan is de x-waarde voor de linker bovenhoek gelijk aan de x-waarde van het midden minus de halve breedte (width) van de minimale rechthoek minus left. De y-waarde van de linker bovenhoek is aan de y-waarde van het midden minus de halve hoogte (height) van de minimale rechthoek minus top.

We moeten ook uitrekenen waar de tekst in het plaatje moet worden geplaatst.

Layout inputfile wijzigen

Ik verzin dat ik met een slash-teken (/) zou willen aangeven, waar de tekst op een nieuwe regel vervolgd moet worden. maar besef dat dat lastig wordt, wanneer er een slash-teken voorkomt in de tekst. Ik bedenk daarom een nieuwe layout voor de records van het bestand rechthoeken.csv. Ik verander de layout in:

upper_left;x;y;width;height;top;right;bottom;left;color;text_x;text_y;sep;text;
;10;5;300;30;10;10;10;10;lightGreen;15;24;;the quick brown fox jumps over the lazy dog;
;10;50;300;30;lightGreen;15;69;;filmquiz bracht knappe ex-yogi van de wijs;
;10;90;325;110;lightGreen;15;120;/;Als beginnend concertist debuteerde een/ fijngevoelige gitarist, hierna improviseerden/jeugdige klankkunstenaars levendig maar/notenblind op Peruviaanse quena’s, robuuste/slagwerkers trommelden uitzinnige volksmuziek,/waarna xylofonisten "e;Yesterday"e; zongen.;